package social.hub.impl.scribe.oauth;

import java.util.Map;

import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.Api;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;

import social.hub.impl.scribe.util.GenericScribe10aAPI;
import social.hub.impl.scribe.util.GenericScribe20API;
import social.hub.oauth.OAuthConfig;
import social.hub.oauth.OAuthProvider;
import social.hub.oauth.OAuthToken;
import social.hub.util.UrlBuilder;
import social.hub.util.Utils;

public class ScribeOAuthProvider implements OAuthProvider {

	private static final Token EMPTY_SCRIBE_TOKEN = new Token("", "");
	private final OAuthService service;
	private final OAuthConfig config;

	private GenericScribe10aAPI oauth1Api;
	private GenericScribe20API oauth2Api;

	public ScribeOAuthProvider(OAuthConfig config) {

		this.config = config;

		Api provider = null;

		if (config.isOAuthVersion2()) {
			oauth2Api = new GenericScribe20API(config);
			provider = oauth2Api;
		} else {
			oauth1Api = new GenericScribe10aAPI(config);
			provider = oauth1Api;
		}

		final ServiceBuilder builder = new ServiceBuilder().provider(provider).apiKey(config.getApiKey())
				.apiSecret(config.getApiSecret());

		if (Utils.isNotNullOrBlank(config.getCallbackUrl())) {
			builder.callback(config.getCallbackUrl());
		}

		if (Utils.isNotNullOrBlank(config.getScope())) {
			builder.scope(config.getScope());
		}

		service = builder.build();

	}

	public OAuthToken getAccessToken(OAuthToken requestToken, Map<String, String> parans) {

		try {

			final Token accessToken = config.isOAuthVersion2() ? getOAuth2AcessToken(parans) : getOAuth1AccessToken(
					requestToken, parans);

			OAuthToken result = null;

			if (!isEmptyToken(accessToken)) {
				result = convertToOAuthToken(accessToken);
			}

			return result;

		} catch (Exception e) {
			throw new RuntimeException("Erro getting access token.", e);
		}

	}

	public String getAuthorizationUrl(OAuthToken requestToken) {

		try {

			Token token = EMPTY_SCRIBE_TOKEN;

			if (!config.isOAuthVersion2()) {
				token = convertToScribeToken(requestToken);
			}

			UrlBuilder builder = new UrlBuilder(service.getAuthorizationUrl(token));

			if (requestToken != null) {
				builder.addUrlParan("requestToken", requestToken.getKey());
				builder.addUrlParan("requestSecret", requestToken.getSecret());
			}

			return builder.toString();

		} catch (Exception e) {
			throw new RuntimeException("Erro getting authentication URL.", e);
		}

	}

	public OAuthToken getRequestToken() {

		try {

			OAuthToken token = null;

			if (!config.isOAuthVersion2()) {
				token = convertToOAuthToken(service.getRequestToken());
			}

			return token;

		} catch (Exception e) {
			throw new RuntimeException("Erro getting request token.", e);
		}

	}

	private OAuthToken convertToOAuthToken(Token token) {
		return new OAuthToken(token.getToken(), token.getSecret());
	}

	private Token convertToScribeToken(OAuthToken token) {
		return new Token(token.getKey(), token.getSecret());
	}

	private Token getOAuth1AccessToken(OAuthToken requestToken, Map<String, String> providerParans) {

		final String verifier = Utils.decode(providerParans.get("oauth_verifier"));

		return service.getAccessToken(convertToScribeToken(requestToken), new Verifier(verifier));

	}

	private Token getOAuth2AcessToken(Map<String, String> parans) {

		String url = oauth2Api.getAccessTokenEndpoint();
		url = url.replace("{code}", parans.get("code"));

		OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, url);
		Response response = oAuthRequest.send();

		return oauth2Api.getAccessTokenExtractor().extract(response.getBody());

	}

	private boolean isEmptyToken(Token token) {
		return token == null || token.getToken() == null || token.getToken().isEmpty();
	}

}
